using System;
using System.Collections.Generic;
using System.Data;
using System.Collections;
using Widgetsphere.Core.EventArgs;

namespace Widgetsphere.Core.DataAccess
{
  public partial class BusinessObjectList<T> : IList<T>, IList, ITyped
  {
    #region Class Members

    public List<T> mBusinessObject;

    #endregion

    #region Constructor

    public BusinessObjectList()
    {
      mBusinessObject = new List<T>();
    }

    public BusinessObjectList(IEnumerable<T> collection)
    {
      mBusinessObject = new List<T>(collection);
    }

    public BusinessObjectList(int capacity)
    {
      mBusinessObject = new List<T>(capacity);
    }

    #endregion

    #region Events

    public event BusinessObjectEventHandler ObjectAdded;
    public event BusinessObjectEventHandler ObjectRemoved;

    protected virtual void OnObjectAdded(BusinessObjectEventArgs e)
    {
      if(this.ObjectAdded != null)
        this.ObjectAdded(this, e);
    }

    protected virtual void OnObjectRemoved(BusinessObjectEventArgs e)
    {
      if(this.ObjectRemoved != null)
        this.ObjectRemoved(this, e);
    }

    #endregion

    #region Expose List Methods
    public void Sort()
    {
      mBusinessObject.Sort();
    }
    public void Sort(Comparison<T> comparison)
    {
      mBusinessObject.Sort(comparison);
    }
    public void Sort(IComparer<T> comparer)
    {
      mBusinessObject.Sort(comparer);
    }
    #endregion

    #region IList Members

    public T this[int index]
    {
      get
      {
        return mBusinessObject[index];
      }
      set
      {
        this[index] = value;
      }
    }

    public void RemoveAt(int index)
    {
      if(ObjectRemoved != null)
      {
        this.OnObjectRemoved(new BusinessObjectEventArgs((IBusinessObject)mBusinessObject[index]));
      }
      mBusinessObject.RemoveAt(index);
    }

    public void Insert(int index, T element)
    {
      this.Insert(index, (IBusinessObject)element);
    }

    public void Insert(int index, IBusinessObject element)
    {
      if(ObjectRemoved != null)
      {
        this.OnObjectRemoved(new BusinessObjectEventArgs((IBusinessObject)mBusinessObject[index]));
      }
      mBusinessObject.Insert(index, (T)element);
      if(ObjectAdded != null)
      {
        this.OnObjectAdded(new BusinessObjectEventArgs((IBusinessObject)mBusinessObject[index]));
      }
    }

    public bool Remove(T element)
    {
      int count = this.Count;
      this.Remove((IBusinessObject)element);
      return this.Count < count;
    }

    public void Remove(IBusinessObject element)
    {
      int indexOf = this.IndexOf(element);
      if(indexOf >= 0)
      {
        this.RemoveAt(indexOf);
      }
    }

    public bool Contains(T element)
    {
      return this.Contains((IBusinessObject)element);
    }

    public bool Contains(IBusinessObject element)
    {
      DataRow row1 = ((DataRow)element.WrappedClass);
      DataColumn[] keys1 = row1.Table.PrimaryKey;
      foreach(IBusinessObject loop in this)
      {
        DataRow row2 = ((DataRow)loop.WrappedClass);
        DataColumn[] keys2 = row2.Table.PrimaryKey;

        //Verify key fields are the same
        if(keys1.Length != keys2.Length)
          throw new Exception("Element types do not match!");
        for(int ii = 0 ; ii < keys1.Length ; ii++)
          if((keys1[ii].ColumnName != keys2[ii].ColumnName) || (keys1[ii].DataType != keys2[ii].DataType))
            throw new Exception("Element types do not match!");

        //Check the value
        foreach(DataColumn column in keys1)
        {
          bool bValue = false;
          //if (column.DataType == typeof(Guid))
          //  bValue = (row1[column.ColumnName].ToString() == row2[column.ColumnName].ToString());
          //else
          //  bValue = (row1[column.ColumnName] == row2[column.ColumnName]);
          bValue = (row1[column.ColumnName].ToString() == row2[column.ColumnName].ToString());
          if(bValue)
            return true;
        }
      }
      return false;
    }

    public void Clear()
    {
      for(int ii = mBusinessObject.Count - 1 ; ii >= 0 ; ii--)
      {
        this.RemoveAt(ii);
      }
      mBusinessObject.Clear();
    }

    public int IndexOf(T element)
    {
      return this.IndexOf((IBusinessObject)element);
    }

    public int IndexOf(IBusinessObject element)
    {
      DataRow row1 = ((DataRow)element.WrappedClass);
      DataColumn[] keys1 = row1.Table.PrimaryKey;
      int index = 0;
      foreach(IBusinessObject loop in this)
      {
        DataRow row2 = ((DataRow)loop.WrappedClass);
        DataColumn[] keys2 = row2.Table.PrimaryKey;

        //Verify key fields are the same
        if(keys1.Length != keys2.Length)
          throw new Exception("Element types do not match!");
        for(int ii = 0 ; ii < keys1.Length ; ii++)
          if((keys1[ii].ColumnName != keys2[ii].ColumnName) || (keys1[ii].DataType != keys2[ii].DataType))
            throw new Exception("Element types do not match!");

        //Check the value
        foreach(DataColumn column in keys1)
        {
          bool bValue = false;
          bValue = (row1[column.ColumnName].ToString() == row2[column.ColumnName].ToString());
          if(bValue)
            return index;
        }

        index++;
      }
      return -1;
    }

    public void Add(T element)
    {
      this.Add((IBusinessObject)element);
    }

    public void Add(IBusinessObject element)
    {
      try
      {
        if(ObjectAdded != null)
        {
          this.OnObjectAdded(new BusinessObjectEventArgs(element));
        }
      }
      catch(InvalidConstraintException ex)
      {
        throw new Widgetsphere.Core.Exceptions.WSException("Framework usage error: The parent item must be added to its collection before creating the relationship.", ex);
      }
      mBusinessObject.Add((T)element);

    }

    public void AddRange(ICollection<T> c)
    {
      foreach(IBusinessObject o in c)
        this.Add(o);
    }

    #endregion

    #region ICollection Members

    public int Count
    {
      get { return mBusinessObject.Count; }
    }

    public void CopyTo(T[] array, int index)
    {
      mBusinessObject.CopyTo(array, index);
    }

    public void CopyTo(Array array, int index)
    {
      ((IList)mBusinessObject).CopyTo(array, index);
    }

    public bool IsReadOnly
    {
      get { return false; }
    }

    public bool IsSynchronized
    {
      get
      {
        return ((IList)mBusinessObject).IsSynchronized;
      }
    }

    public object SyncRoot
    {
      get
      {
        return ((IList)mBusinessObject).SyncRoot;
      }
    }

    #endregion

    #region IEnumerable Members
    IEnumerator<T> IEnumerable<T>.GetEnumerator()
    {
      return ((IEnumerable<T>)mBusinessObject).GetEnumerator();
    }

    public System.Collections.IEnumerator GetEnumerator()
    {
      return mBusinessObject.GetEnumerator();
    }

    #endregion

    #region ITyped Members

    Type ITyped.GetContainedType()
    {
      return typeof(T);
    }

    #endregion

    #region Collection Event Handlers
    private void CollectionObjectAdded(object sender, IBusinessObject row)
    {
      this.Add(row);
    }

    private void CollectionObjectRemoved(object sender, IBusinessObject row)
    {
      this.Remove(row);
    }
    #endregion

    #region IList Members

    public int Add(object value)
    {
      return ((IList)mBusinessObject).Add(value);
    }

    public bool Contains(object value)
    {
      return ((IList)mBusinessObject).Contains(value);
    }

    public int IndexOf(object value)
    {
      return ((IList)mBusinessObject).IndexOf(value);
    }

    public void Insert(int index, object value)
    {
      ((IList)mBusinessObject).Insert(index, value);
    }

    public bool IsFixedSize
    {
      get
      {
        return ((IList)mBusinessObject).IsFixedSize;
      }
    }

    public void Remove(object value)
    {
      this.OnObjectRemoved(new BusinessObjectEventArgs((IBusinessObject)value));
      ((IList)mBusinessObject).Remove(value);
    }

    object IList.this[int index]
    {
      get
      {
        return ((IList)mBusinessObject)[index];
      }
      set
      {
        ((IList)mBusinessObject)[index] = value;
      }
    }

    #endregion

    #region Group Methods

    public BusinessObjectList<T> Union(BusinessObjectList<T> list)
    {
      BusinessObjectList<T> retval = new BusinessObjectList<T>();
      retval.AddRange(this);
      foreach(T o in list)
      {
        if(!retval.Contains(o))
          retval.Add(o);
      }
      return retval;
    }

    public BusinessObjectList<T> Intersect(BusinessObjectList<T> list)
    {
      BusinessObjectList<T> retval = new BusinessObjectList<T>();
      foreach(T o in list)
      {
        if(this.Contains(o))
          retval.Add(o);
      }
      return retval;
    }

    public BusinessObjectList<T> Subtract(BusinessObjectList<T> list)
    {
      BusinessObjectList<T> retval = new BusinessObjectList<T>();
      retval.AddRange(this);
      foreach(T o in list)
      {
        while(retval.Contains(o))
          retval.Remove(o);
      }
      return retval;
    }

    #endregion

  }

  public delegate void BusinessObjectEventHandler(object sender, BusinessObjectEventArgs e);

}